package com.shadow.demo.excel.demo.v2.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.shadow.demo.common.enums.excel.ImportCheckEnum;
import com.shadow.demo.data.excel.entity.EasyExcelEntity;
import com.shadow.demo.excel.demo.v2.listener.annotation.ImportCheck;
import com.shadow.demo.excel.easy.excel.MyExcelException;
import com.shadow.demo.excel.service.EasyExcelService;
import com.shadow.demo.excel.demo.v2.vo.EasyExcelImportVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 监听类.
 * EasyExcel作者说:
 *      有个很重要的点 DemoDataListener 不能被spring管理
 *      要每次读取excel都要new
 *      然后里面用到spring可以构造方法传进去
 * 固定模板
 * 1. invokeHead
 * 2. invoke
 * */
@Slf4j
public class EasyExcelListener implements ReadListener<EasyExcelImportVO> {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 3;

    /* 字段校验结果 */
    private final Map<String, String> result = new HashMap<>();
    /* service */
    private final EasyExcelService easyExcelService;
    /* 缓存的数据 */
    private final List<EasyExcelEntity> cachedDataList = new ArrayList<>(BATCH_COUNT);

    public EasyExcelListener(final EasyExcelService easyExcelService) {
        this.easyExcelService = easyExcelService;
    }

    /**
     * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
     * entire read will terminate.
     *
     * @param exception 异常
     * @param context excel内容
     * @throws Exception 异常
     */
    @Override
    public void onException(final Exception exception, final AnalysisContext context) throws Exception {
        log.info("onException");
        throw exception;
//        result.put(context.readRowHolder().getRowIndex().toString(), ((MyException) exception).getMsg());
    }

    /**
     * When analysis one head row trigger invoke function.
     *
     * @param headMap 表头
     * @param context excel内容
     */
    @Override
    public void invokeHead(final Map<Integer, ReadCellData<?>> headMap, final AnalysisContext context) {
        // 验证表头
        String head1 = "表头1";
        String head2 = "表头2";
        if (headMap.isEmpty()) {
//            errMap.put("msg", "导入的模板不符合,请检查后重新导入!");
        }
        //
        if (!headMap.containsValue(head1) || !headMap.containsValue(head2)) {
//            errMap.put("msg", "导入的模板不符合,请检查后重新导入!");
        }
    }

    @Override
    public void invoke(final EasyExcelImportVO easyExcelImportVO, final AnalysisContext context) {
        String r = check(context.readRowHolder().getRowIndex(), easyExcelImportVO);
        if (StringUtils.isNotBlank(r)) {
            cachedDataList.add(null);
        } else {
            EasyExcelEntity easyExcelEntity = new EasyExcelEntity();
            BeanUtils.copyProperties(easyExcelImportVO, easyExcelEntity);
            cachedDataList.add(easyExcelEntity);
        }
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            submit();
        }
    }

    @Override
    public void doAfterAllAnalysed(final AnalysisContext context) {
        if (!cachedDataList.isEmpty()) {
            submit();
        }
        result.clear();
        log.info("success");
    }

    /**
     * 校验
     */
    private String check(final Integer index, final EasyExcelImportVO easyExcelImportVO) {
        final Map<String, String> r = new HashMap<>();
        for (Field field : easyExcelImportVO.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(ImportCheck.class)) {
                ImportCheck importCheck = field.getAnnotation(ImportCheck.class);
                // 设置字段可访问
                field.setAccessible(true);
                try {
                    Object value = field.get(easyExcelImportVO);
                    for (ImportCheckEnum importCheckEnum : importCheck.type()) {

                        String a = ImportCheckEnum.execute(importCheckEnum, value, null);
                        if (StringUtils.isNotBlank(a)) {
                            r.put(field.getName(), a);
                        }
                    }
                } catch (IllegalAccessException e) {
                    r.put(field.getName(), "get field error");
                    log.error("get field[{}] error", field.getName(), e);
                }
            }
        }
        if (r.isEmpty()) {
            return null;
        } else {
            result.put(index.toString(), "[" + formatCheckResult(r) + "]");
            return "error";
        }
    }

    /**
     * 提交
     */
    private void submit() {
        if (!result.isEmpty()) {
            throw new MyExcelException(formatCheckResult(result));
        }
        boolean r = easyExcelService.saveBatch(cachedDataList);
        if (r) {
            log.info("存储数据库成功！");
        }
        cachedDataList.clear();
    }
//    /**
//     * 加上存储数据库
//     */
//    private void saveData() {
//        boolean r = easyExcelService.saveBatch(cachedDataList);
//        if (r) {
//            log.info("存储数据库成功！");
//        }
//    }

    private String formatCheckResult(final Map<?, ?> map) {
        if (MapUtils.isEmpty(map)) {
            return null;
        }
        StringBuilder stringBuilder = new StringBuilder();
        map.forEach((k, v) -> stringBuilder.append(k)
                .append(": ")
                .append(v)
                .append(", ")
        );
        return stringBuilder.substring(0, stringBuilder.length() - 2);
    }
}
